using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;
using CS6238Project2.Banking.Communication;
using CS6238Project2.Banking.Commerce;
using System.IO;
using System.Xml.Serialization;


namespace CS6238Project2.Banking.Tools
{
    /// <summary>
    /// Creates, completes, and verifies certificates
    /// </summary>
    static class Encryption
    {
        //http://msdn2.microsoft.com/en-us/library/system.security.cryptography.rsacryptoserviceprovider.aspx



        public static void D_Write(Token token)
        {
            string iv2 = "";
            string key2 = "";

            for (int i = 0; i < token.AES.IV.Length; i++)
            {
                iv2 += Convert.ToInt16(token.AES.IV[i]);
            }
            for (int i = 0; i < token.AES.Key.Length; i++)
            {
                key2 += Convert.ToInt16(token.AES.Key[i]);
            }

            Console.WriteLine("DECRYPTION  IV : " + iv2);
            Console.WriteLine("DECRYPTION  KEY : " + key2);
            Console.WriteLine("DECRYPTION  PD : " + token.AES.Padding.ToString());
        }


        public static void E_Write(Token token)
        {
            string iv2 = "";
            string key2 = "";

            for (int i = 0; i < token.AES.IV.Length; i++)
            {
                iv2 += Convert.ToInt16(token.AES.IV[i]);
            }
            for (int i = 0; i < token.AES.Key.Length; i++)
            {
                key2 += Convert.ToInt16(token.AES.Key[i]);
            }

            Console.WriteLine("ENCRYPTION  IV : " + iv2);
            Console.WriteLine("ENCRYPTION  KEY : " + key2);
            Console.WriteLine("ENCRYPTION  PD : " + token.AES.Padding.ToString());
        }
        


        /// <summary>
        /// Decrypts into XmlString form an encrypted XmlDoc given a key 
        /// </summary>
        /// <param name="xmlDoc">The document to be decrypted</param>
        /// <param name="token">the key</param>
        /// <returns>an XML string</returns>
        public static string Decrypt(XmlDocument xmlDoc, Token token)
        {
            D_Write(token);
            
            XmlElement encryptedElement;
            EncryptedData encryptedData = new EncryptedData();
            EncryptedXml eXml = new EncryptedXml();
            int count = xmlDoc.GetElementsByTagName("EncryptedData").Count;
            encryptedElement = xmlDoc.GetElementsByTagName("EncryptedData")[0] as XmlElement;

            encryptedData.Type = EncryptedXml.XmlEncElementUrl;
            //set the encryption menthod
            encryptedData.EncryptionMethod =
                new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
            //Set the ciphervalue of the encrypted element


            encryptedData.LoadXml(encryptedElement);
            byte[] plaintext = eXml.DecryptData(encryptedData, token.AES);
            eXml.ReplaceData(encryptedElement, plaintext);
            return XmlString(xmlDoc);
        }


        /// <summary>
        /// ObjectifyXML
        /// </summary>
        /// <param name="xmlDoc">the document to be decrypted</param>
        /// <param name="ty">The Type of object to be returned</param>
        /// <param name="token">the Key to decrypt</param>
        /// <returns>A decrypted object</returns>
        public static object ObjectifyXml(XmlDocument xmlDoc, Type ty, Token token)
        {
            /*these are the setup tools*/
            //use decrypt method to turn the doc into an xml string
            String xmlString = Tools.Encryption.Decrypt(xmlDoc, token);
            //create a serializer for the type
            XmlSerializer serializer = new XmlSerializer(ty);
            //put the string into a stream
            MemoryStream stream =
                new MemoryStream(Tools.Encryption.StringToUTF8ByteArray(xmlString));
            //put the stream into a writer
            XmlTextWriter xmlTextWriter = new XmlTextWriter(stream, Encoding.UTF8);


            try
            {
                //deserialize the writer/stream
                return serializer.Deserialize(stream);
            }
            catch (XmlException e)
            {
                Console.WriteLine(e.Message);
                return new Object();
            }
        }

        public static XmlDocument Encrypt(Object masterObject, Token token)
        {
            /*****               Set up Serialization tools             *****/
            E_Write(token);
            Object obj = masterObject;
            //Set up the tools
            XmlDocument xmlDoc = new XmlDocument();
            EncryptedXml eXml = new EncryptedXml();
            //EncryptedData encryptedElement; //encrypted XML elements
            

            /****                 PUT ITEM IN XMLDOC                     ****/
            xmlDoc.LoadXml(XmlString(obj));


            //Assure that there is an encryption key present
            //if there isn't, just the document will be returned.
            if (token != null)
            {

                    /****                ENCRYPT ALL ELEMENTS                    ****/
                XmlElement element;
                EncryptedData encryptedData;


                string iv2 = "";
                string key2 = "";

                for (int i = 0; i < token.AES.IV.Length; i++)
                {
                    iv2 += Convert.ToInt16(token.AES.IV[i]);
                }
                for (int i = 0; i < token.AES.IV.Length; i++)
                {
                    key2 += Convert.ToInt16(token.AES.Key[i]);
                }

                Console.WriteLine("ENCRYPTION  IV : " + iv2);
                Console.WriteLine("ENCRYPTION  KEY : " + key2);
                Console.WriteLine("ENCRYPTION  PD : " + token.AES.Padding.ToString());
                
                element = xmlDoc.GetElementsByTagName("*")[0] as XmlElement;
                //make a new encrypted data object
                encryptedData = new EncryptedData();

                //set its XML url
                encryptedData.Type = EncryptedXml.XmlEncElementUrl;
                //set the encryption menthod

                encryptedData.EncryptionMethod =
                    new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
                //Set the ciphervalue of the encrypted element
                

                
                encryptedData.CipherData.CipherValue
                    = eXml.EncryptData(element, token.AES, true);
                string meow = encryptedData.CipherData.GetXml().InnerXml;

                //replace the element in the XMLdoc with the element
                EncryptedXml.ReplaceElement(element, encryptedData, true);
                

            }
            /*String meow1 = xmlDoc.InnerXml;
            string meow2 = xmlDoc.InnerXml.Trim();
            bool whitespace = meow2.Contains(" ");
            string meow3 = meow2.Trim();*/

            string meow2 = xmlDoc.InnerXml;
            
            char [] whiteSpace = new char[1];
            whiteSpace[0] = ' ';
            meow2 = meow2.Trim(whiteSpace);
            bool whitespace =  meow2.Contains(" ");
            

            xmlDoc.InnerXml = meow2;
            return xmlDoc;
        }



        /// <summary>
        /// Sourced from:
        /// http://www.eggheadcafe.com/articles/system.xml.xmlserialization.asp
        /// This method turns a UTF byte array into a string
        /// 
        /// </summary>
        /// <param name="characters">The byte array to be converted</param>
        /// <returns>A string in UTF8</returns>
        public static String UTF8ByteArrayToString(byte[] characters)
        {

            UTF8Encoding encoding = new UTF8Encoding(false, true);
            String constructedString = encoding.GetString(characters);
            return (constructedString);
        }

        /// <summary>
        /// Sourced from the same place, turns the string into a byte array
        /// </summary>
        /// <param name="pXmlString">A string representation of xml</param>
        /// <returns>a byte array of that string</returns>
        public static byte[] StringToUTF8ByteArray(String pXmlString)
        {
            UTF8Encoding encoding = new UTF8Encoding(false, true);
            byte[] byteArray = encoding.GetBytes(pXmlString);
            return byteArray;
        }


        /// <summary>
        /// Turns an object into an XML string, which is quite useful!
        /// </summary>
        /// <param name="obj">The object to be transformed</param>
        /// <returns>The String representing the XML</returns>
        public static string XmlString(Object obj)
        {

            Type theType = obj.GetType();
            XmlSerializer serializer = new XmlSerializer(theType);
            String xmlString = null;//will store the xml string
            MemoryStream stream = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8);
            serializer.Serialize(writer, obj);//Put the object in a writer
            stream = (MemoryStream)writer.BaseStream;//export the writer to a stream

            xmlString = UTF8ByteArrayToString(stream.ToArray());//turn the stream into a string
            return xmlString.Trim();
        }


        /// <summary>
        /// This method accepts an xml string and turns it into an object
        /// </summary>
        /// <param name="xmlString">The string to be deserizlied</param>
        /// <param name="type">The type of object that it is</param>
        /// <returns>an object that's been serialized</returns>
        public static object Destring(string xmlString, Type type)
        {
            XmlSerializer serializer = new XmlSerializer(type);
            //put the string into a stream
            MemoryStream stream =
                new MemoryStream(Tools.Encryption.StringToUTF8ByteArray(xmlString));
            //put the stream into a writer
            XmlTextWriter xmlTextWriter = new XmlTextWriter(stream, Encoding.UTF8);

            try
            {
                //deserialize the writer/stream
                return serializer.Deserialize(stream);
            }
            catch (XmlException e)
            {
                Console.WriteLine(e.Message);
                return null;
            }
        }


    }
}
